package beautiful.butterfly.distributed_service_container.http.mvc.core;

import beautiful.butterfly.distributed_service_container.http.mvc.aop.Interceptor;
import beautiful.butterfly.distributed_service_container.http.mvc.aop.InterceptorBuilder;
import beautiful.butterfly.distributed_service_container.http.mvc.core.annotation.actionbind.ActionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.*;


public final class ActionMapping
{

    @SuppressWarnings("unused")
    private static Logger logger = LoggerFactory.getLogger(ActionMapping.class);
    private static Map<String, Action> actionMapping = new HashMap<String, Action>();

    public static Action getAction(String key)
    {
        Action action = actionMapping.get(key);
        if (action != null)
        {
            return action;
        }
        return null;
    }


    @SuppressWarnings("unchecked")
    public static void buildActionMapping(String basePackageName)
    {
        // 需要排除的方法
        Set<String> publicAndNoParamtersMethodNamesOfControllerClass = findPublicAndNoParamtersMethodNamesInControllerClass();

        InterceptorBuilder interceptorBuilder = new InterceptorBuilder();
        /**
         * 获取所有的标记含有控制器注解的类，均生成实例
         */
        //String[] packageNames = ActionMapping.class.getName().split("\\.");
        //String basePackageName = packageNames[0] + "." + packageNames[1];
        System.out.println("basePackageName:" + basePackageName);
        List<Class> controllerClassList = ClassSearcher.findSubClass(Controller.class, basePackageName);
        for (Class<Controller> clazz : controllerClassList)
        {
            Controller controller;
            try
            {
                // 引用一直会保存
                controller = (Controller) clazz.newInstance();
            } catch (InstantiationException e)
            {
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (IllegalAccessException e)
            {
                e.printStackTrace();
                throw new RuntimeException(e);
            }

            ActionKey actionKey = clazz.getAnnotation(ActionKey.class);
            if (actionKey == null)
            {
                throw new IllegalArgumentException("在控制器上面actionKey注解不存在:where" + clazz.getName());
            }
            String key = actionKey.value();
            if (key == null || "".equals(key))
            {
                throw new IllegalArgumentException("在控制器上面actionKey注解为空");
            }
            if (!key.startsWith("/"))
            {
                throw new IllegalArgumentException("在控制器上面actionKey不是以/开头");
            }
            // 构建控制器上面的拦截器
            List<Interceptor> controllerInterceptorList = interceptorBuilder
                    .buildControllerInterceptors(clazz);
            // 然后对每个方法进行处理

            Method[] methods = clazz.getMethods();

            for (Method method : methods)
            {

                String methodName = method.getName();
                // 判断是否是Action的方法是：没有出现和Controller父类中的方法同名的，且无参数的方法
                // 无参数判断条件可以提高判断的效率
                if ((method.getParameterTypes().length == 0)
                        && (!publicAndNoParamtersMethodNamesOfControllerClass.contains(methodName)))
                {
                    List<Interceptor> methodInterceptorList = interceptorBuilder
                            .buildMethodInterceptors(method);

                    List<Interceptor> actionInterceptorList = interceptorBuilder
                            .buildActionInterceptors(clazz, controllerInterceptorList,
                                    method, methodInterceptorList);
                    // Action的构建方式是: controller+getMethod

                    ActionKey actionKeyOnMethed = method.getAnnotation(ActionKey.class);
                    String actionKeyValue = "";
                    if (actionKeyOnMethed != null)
                    {// 如果支持则不使用方法名作为url第二个部分
                        String actionKeyOnMethedValue = actionKeyOnMethed.value();
                        if (actionKeyOnMethedValue != null && !"".equals(actionKeyOnMethedValue))
                        {
                            if (!actionKeyOnMethedValue.startsWith("/"))
                            {
                                throw new IllegalArgumentException("在方法上面actionKey不是以/开头");
                            }
                            actionKeyValue = key + actionKeyOnMethed.value();
                            if (actionMapping.containsKey(actionKeyValue))
                            {
                                warnning(actionKeyValue, clazz, method);
                                throw new RuntimeException(
                                        "Action:" + actionKey + "已经存在. Controller/Method所对应的action重复.");
                            }

                            Action action = new Action(actionKeyValue, key, clazz, controller, methodName, method,
                                    actionInterceptorList);
                            actionMapping.put(actionKeyValue, action);
                        } else
                        {
                            // 普通方法
                            continue;

                        }

                    } else
                    {
                        // 必须含有分割线
                        actionKeyValue = key + "/" + method.getName();
                        if (actionMapping.containsKey(actionKeyValue))
                        {
                            warnning(actionKeyValue, clazz, method);
                            throw new RuntimeException(
                                    "Action:" + actionKey + "已经存在. Controller/Method所对应的action重复.");
                        }

                        Action action = new Action(actionKeyValue, key, clazz, controller, methodName, method,
                                actionInterceptorList);
                        actionMapping.put(actionKeyValue, action);
                    }

                }
            }// for (Method getMethod : methods)

        }

    }

    /**
     * 取得Controller.class中参数个数为0，访问修饰符为public的方法集合
     */
    private static Set<String> findPublicAndNoParamtersMethodNamesInControllerClass()
    {
        Set<String> publicAndNoParamtersMethodNames = new HashSet<String>();
        Method[] methods = Controller.class.getMethods();
        for (Method method : methods)
        {
            // 不要含有参数
            if (method.getParameterTypes().length == 0)
            {
                publicAndNoParamtersMethodNames.add(method.getName());
            }
        }
        // jrebel产生的代理方法__rebel_clinit,需要将此方法排除
        publicAndNoParamtersMethodNames.add("__rebel_clinit");
        return publicAndNoParamtersMethodNames;
    }

    /**
     * 警告但是不终止程序
     */
    private static final void warnning(String actionKey, Class<? extends Controller> controllerClass,
                                       Method method)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("\n警告:ActionKey:" + actionKey + "已经被使用.");
        sb.append("ActionKey不能匹配方法:");
        sb.append(controllerClass.getName());
        sb.append(".");
        sb.append(method.getName());
        sb.append("()");
        if (logger.isWarnEnabled())
        {
            logger.warn(sb.toString());
        }
    }

}
